package engine

import (
	"gitee.com/kinwyb/conv"
	"gitee.com/kinwyb/workflow/public"
	"gitee.com/kinwyb/workflow/storage"
	"github.com/tidwall/gjson"
	"strings"
)

type IRouteFormula interface {
	// Symbol 表达式符号
	Symbol() string
	// Calculate 公式计算,返回是否
	Calculate(formual string, jsonData string) bool
}

var routeFormulas []IRouteFormula

// RegisterRouteFormula 注册路由解析公式
func RegisterRouteFormula(rf IRouteFormula) {
	routeFormulas = append(routeFormulas, rf)
}

func registerDefaultRouteFormula() {
	RegisterRouteFormula(&greaterOrEqualFormula{})
	RegisterRouteFormula(&lessOrEqualFormula{})
	RegisterRouteFormula(&greaterFormula{})
	RegisterRouteFormula(&lessFormula{})
	RegisterRouteFormula(&equalFormula{})
}

type equalFormula struct {
}

func (equalFormula) Symbol() string {
	return "="
}

func (equalFormula) Calculate(formula string, jsonData string) bool {
	formuals := strings.Split(formula, "=")
	field1 := strings.TrimSpace(formuals[0])
	fieldVal1 := gjson.Get(jsonData, field1)
	if !fieldVal1.Exists() { //内容不存在
		return false
	}
	if len(formuals) == 1 {
		return fieldVal1.Bool()
	}
	if len(formuals) != 2 {
		return false
	}
	field2 := strings.TrimSpace(formuals[1])
	switch fieldVal1.Type {
	case gjson.String:
		return fieldVal1.String() == conv.ToString(field2)
	case gjson.Number:
		return fieldVal1.Float() == conv.ToFloat64(field2)
	case gjson.True, gjson.False:
		return fieldVal1.Bool() == conv.ToBool(field2)
	default:
		return false
	}
}

type greaterFormula struct{}

func (greaterFormula) Symbol() string {
	return ">"
}

func (greaterFormula) Calculate(formula string, jsonData string) bool {
	formuals := strings.Split(formula, ">")
	if len(formuals) != 2 {
		return false
	}
	field1 := strings.TrimSpace(formuals[0])
	field2 := strings.TrimSpace(formuals[1])
	fieldVal1 := gjson.Get(jsonData, field1)
	if !fieldVal1.Exists() { //内容不存在
		return false
	}
	switch fieldVal1.Type {
	case gjson.Number:
		return fieldVal1.Float() > conv.ToFloat64(field2)
	default:
		return false
	}
}

type lessFormula struct{}

func (lessFormula) Symbol() string {
	return "<"
}

func (lessFormula) Calculate(formula string, jsonData string) bool {
	formuals := strings.Split(formula, "<")
	if len(formuals) != 2 {
		return false
	}
	field1 := strings.TrimSpace(formuals[0])
	field2 := strings.TrimSpace(formuals[1])
	fieldVal1 := gjson.Get(jsonData, field1)
	if !fieldVal1.Exists() { //内容不存在
		return false
	}
	switch fieldVal1.Type {
	case gjson.Number:
		return fieldVal1.Float() < conv.ToFloat64(field2)
	default:
		return false
	}
}

type greaterOrEqualFormula struct{}

func (greaterOrEqualFormula) Symbol() string {
	return ">="
}

func (greaterOrEqualFormula) Calculate(formula string, jsonData string) bool {
	formualGreater := strings.ReplaceAll(formula, ">=", ">")
	formualEqual := strings.ReplaceAll(formula, ">=", "=")
	return greaterFormula{}.Calculate(formualGreater, jsonData) || equalFormula{}.Calculate(formualEqual, jsonData)
}

type lessOrEqualFormula struct{}

func (*lessOrEqualFormula) Symbol() string {
	return "<="
}

func (*lessOrEqualFormula) Calculate(formula string, jsonData string) bool {
	formualLess := strings.ReplaceAll(formula, "<=", "<")
	formualEqual := strings.ReplaceAll(formula, "<=", "=")
	return lessFormula{}.Calculate(formualLess, jsonData) || equalFormula{}.Calculate(formualEqual, jsonData)
}

// 解析路由
func routeParse(route FlowRoute, data string) bool {
	if data == "" {
		return false
	}
	if len(routeFormulas) < 1 {
		registerDefaultRouteFormula()
	}
	var res bool
	var index = -1
	for idx, v := range routeFormulas {
		if strings.Contains(route.Formula, v.Symbol()) {
			res = v.Calculate(route.Formula, data)
			index = idx
			break
		}
	}
	if index < 0 {
		res = equalFormula{}.Calculate(route.Formula, data)
	}
	isOr := strings.ToLower(route.ActType) == "or"
	if isOr && res {
		return true
	} else if !isOr && !res {
		return false
	}
	for _, v := range route.Children {
		if v != nil {
			if isOr {
				res = res || routeParse(*v, data)
			} else {
				res = res && routeParse(*v, data)
			}
		}
	}
	return res
}

// 路由公式解析
func routeFormulaString(route FlowRoute) string {
	ret := route.Formula
	cCount := len(route.Children)
	if cCount > 0 {
		ret = "(" + ret
	}
	if cCount > 0 {
		isOr := strings.ToLower(route.ActType) == "or"
		if isOr {
			ret += " OR "
		} else {
			ret += " AND "
		}
	}
	for idx, v := range route.Children {
		if v != nil {
			if idx > 0 {
				isOr := strings.ToLower(route.ActType) == "or"
				if isOr {
					ret += " OR "
				} else {
					ret += " AND "
				}
			}
			ret += routeFormulaString(*v)
		}
	}
	if cCount > 0 {
		ret += " )"
	}
	return ret
}

func routeToNode(route FlowRoute, topRouteID string) ([]storage.RouteNode, error) {
	if topRouteID == "" {
		topRouteID = route.RouteID
	}
	routeNode := storage.RouteNode{
		TopID:    topRouteID,
		RouteID:  route.RouteID,
		ParentID: route.ParentID,
		Formula:  route.Formula,
		ActType:  route.ActType,
	}
	if route.ActType == "" {
		route.ActType = "and"
	}
	var childrenNodes []storage.RouteNode
	for _, v := range route.Children {
		cnodes, err := routeToNode(*v, topRouteID)
		if err != nil {
			return nil, err
		}
		childrenNodes = append(childrenNodes, cnodes...)
	}
	return append([]storage.RouteNode{routeNode}, childrenNodes...), nil
}

func routeBuildID(route *FlowRoute) {
	if route != nil {
		route.RouteID = public.IDGenIntString()
		route.Desc = routeFormulaString(*route)
		for idx, v := range route.Children {
			v.ParentID = route.RouteID
			routeBuildID(v)
			route.Children[idx] = v
		}
	}
}

func RouteQuery(routeID string) (*FlowRoute, error) {
	ros, err := storage.RouteQuery(routeID)
	if err != nil {
		return nil, err
	}
	// 查询结果处理
	routeChildren := map[string][]string{}
	routNodeMap := map[string]storage.RouteNode{}
	for _, v := range ros {
		routNodeMap[v.RouteID] = v
		if v.ParentID != "" {
			chd := routeChildren[v.ParentID]
			routeChildren[v.ParentID] = append(chd, v.RouteID)
		}
	}
	topNode := routNodeMap[routeID]
	workRoute := FlowRoute{
		RouteID:  topNode.RouteID,
		ParentID: topNode.ParentID,
		Formula:  topNode.Formula,
		ActType:  topNode.ActType,
	}
	workRoute.Children = flowRouteChildren(workRoute.RouteID, routeChildren, routNodeMap)
	return &workRoute, nil
}

func flowRouteChildren(routeID string, routeChildren map[string][]string, routNodeMap map[string]storage.RouteNode) []*FlowRoute {
	childrenIDs := routeChildren[routeID]
	if len(childrenIDs) < 1 {
		return nil
	}
	var ret []*FlowRoute
	for _, v := range childrenIDs {
		node := routNodeMap[v]
		workRoute := FlowRoute{
			RouteID:  node.RouteID,
			ParentID: node.ParentID,
			Formula:  node.Formula,
			ActType:  node.ActType,
		}
		workRoute.Children = flowRouteChildren(workRoute.RouteID, routeChildren, routNodeMap)
		ret = append(ret, &workRoute)
	}
	return ret
}
